/*
*********************************************************************************************************
*
*	模块名称 : 按键检测模块 (外部中断IO)
*	文件名称 : bsp_key.c
*	版    本 : V1.3
*	说    明 : 扫描检测按键状态，具有消抖功能，支持FIFO队列以及按键记录功能
*				(1) 按键按下
*				(2) 按键弹起
*				(3) 按键长按
*				(4) 按键连发
*
*	修改记录 :
*		版本号  日期        作者     说明
*		V1.0    2013-02-01 armfly  正式发布
*		V1.1    2013-06-29 armfly  增加1个组合键，将bsp_Idle() 函数移到读取系统节拍计数器函数
*								   支持 K1 K2 组合键 与 K2 K3 组合键作为系统组合键
*		V1.2	2016-01-25 armfly  增加P02复用按键. 使用gpio模式直接读取方式
*		V1.3	2018-11-26 armfly  s_tBtn结构体初始化0
*
*	Copyright (C), 2016-2020, 安富莱电子 www.armfly.com
*
*********************************************************************************************************
*/

// #include "bsp.h"
#include "fml_key.h" // 添加兼容层头文件
#ifdef GD32
/* 定义按键GPIO */
typedef struct
{
	uint32_t gpio;
	uint16_t pin;
	uint8_t ActiveLevel; /* 激活电平 */
} X_GPIO_T;
#endif
#ifdef STM32
typedef struct
{
	GPIO_TypeDef *gpio;
	uint16_t pin;
	uint8_t ActiveLevel; /* 激活电平 */
} X_GPIO_T;
#endif

/* GPIO的PIN定义 */
static const X_GPIO_T s_gpio_list[HARD_KEY_NUM] = {
	{GPIOA, GPIO_PIN_0, 1}, /* K1 */
							// {GPIOC, GPIO_PIN_13, 0}, /* K2 */
							// {GPIOH, GPIO_PIN_4, 0},	 /* K3 */
							// {GPIOG, GPIO_PIN_2, 0},	 /* JOY_U */
							// {GPIOB, GPIO_PIN_0, 0},	 /* JOY_D */
							// {GPIOG, GPIO_PIN_3, 0},	 /* JOY_L */
							// {GPIOG, GPIO_PIN_7, 0},	 /* JOY_R */
							// {GPIOI, GPIO_PIN_11, 0}, /* JOY_OK */
};

/* 定义一个按键结构体变量，用于记录各个按键的状态 */
static KEY_T s_tBtn[KEY_COUNT] = {0};
static KEY_FIFO_T s_tKey; /* 按键FIFO结构体 */

static void bsp_InitKeyVar(void);
static void bsp_InitKeyHard(void);
static void bsp_DetectKey(uint8_t i);

/*
*********************************************************************************************************
*	函 数 名: KeyPinActive
*	功能说明: 判断按键是否按下
*	形    参: 无
*	返 回 值: 返回值1 表示按下(普通情况下0表示未按下，反之)
*********************************************************************************************************
*/
static uint8_t KeyPinActive(uint8_t _id)
{
	if (fml_gpio_read_pin(s_gpio_list[_id].gpio, s_gpio_list[_id].pin) == s_gpio_list[_id].ActiveLevel)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

/*
*********************************************************************************************************
*	函 数 名: IsKeyDownFunc
*	功能说明: 判断按键是否按下。可以检测单键、组合键，支持记录连击次数，支持按键长按
*	形    参: 无
*	返 回 值: 返回值1 表示按下(普通情况下0表示未按下，反之)
*********************************************************************************************************
*/
static uint8_t IsKeyDownFunc(uint8_t _id)
{
	/* 实体键 */
	if (_id < HARD_KEY_NUM)
	{
		uint8_t i;
		uint8_t count = 0;
		uint8_t save = 255;

		/* 检测有几个按键按下 */
		for (i = 0; i < HARD_KEY_NUM; i++)
		{
			if (KeyPinActive(i))
			{
				count++;
				save = i;
			}
		}

		if (count == 1 && save == _id)
		{
			return 1; /* 只有1个按键按下时有效 */
		}

		return 0;
	}

	/* 组合键 K1K2 */
	if (_id == HARD_KEY_NUM + 0)
	{
		if (KeyPinActive(KID_K1) && KeyPinActive(KID_K2))
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}

	return 0;
}

/*
*********************************************************************************************************
*	函 数 名: bsp_InitKey
*	功能说明: 初始化按键. 该函数被 bsp_Init() 调用。
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
void bsp_InitKey(void)
{
	bsp_InitKeyVar();  /* 初始化按键变量 */
	bsp_InitKeyHard(); /* 初始化按键硬件 */
}

/*
*********************************************************************************************************
*	函 数 名: bsp_InitKeyHard
*	功能说明: 配置按键对应的GPIO
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
static void bsp_InitKeyHard(void)
{
	//	GPIO_InitTypeDef gpio_init;
	uint8_t i;

	/* 第1步：使能GPIO时钟 */
	FML_KEY_GPIO_CLK_ENABLE();

	/* 第2步：将所有按键GPIO配置为输入模式(实际上CPU复位后就是输入状态) */
	for (i = 0; i < HARD_KEY_NUM; i++)
	{
		fml_gpio_init(s_gpio_list[i].gpio, s_gpio_list[i].pin);
	}
}

/*
*********************************************************************************************************
*	函 数 名: bsp_InitKeyVar
*	功能说明: 初始化按键变量
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
static void bsp_InitKeyVar(void)
{
	uint8_t i;

	/* 对按键FIFO读写指针清零 */
	s_tKey.Read = 0;
	s_tKey.Write = 0;
	s_tKey.Read2 = 0;

	/* 对每个按键结构体成员变量赋一个缺省值 */
	for (i = 0; i < KEY_COUNT; i++)
	{
		s_tBtn[i].LongTime = KEY_LONG_TIME;	   /* 长按时长 0 表示不检测长按事件 */
		s_tBtn[i].Count = KEY_FILTER_TIME / 2; /* 滤波计数值设置为滤波时间的一半 */
		s_tBtn[i].State = 0;				   /* 设置缺省状态为0为未按下 */
		s_tBtn[i].RepeatSpeed = 0;			   /* 连发速度，0表示不支持连发 */
		s_tBtn[i].RepeatCount = 0;			   /* 连发计数器 */
	}

	/* 如果需要改变某些按键的参数，可以在该处重新赋值 */

	/* 摇杆方向键支持连发1次自动重载 */
	//	bsp_SetKeyParam(KID_JOY_U, 100, 6);
	//	bsp_SetKeyParam(KID_JOY_D, 100, 6);
	//	bsp_SetKeyParam(KID_JOY_L, 100, 6);
	//	bsp_SetKeyParam(KID_JOY_R, 100, 6);
}

/*
*********************************************************************************************************
*	函 数 名: bsp_PutKey
*	功能说明: 将1个键值压入按键FIFO缓冲区末尾，主要用于模拟一个按键。
*	形    参:  _KeyCode : 键值
*	返 回 值: 无
*********************************************************************************************************
*/
void bsp_PutKey(uint8_t _KeyCode)
{
	s_tKey.Buf[s_tKey.Write] = _KeyCode;

	if (++s_tKey.Write >= KEY_FIFO_SIZE)
	{
		s_tKey.Write = 0;
	}
}

/*
*********************************************************************************************************
*	函 数 名: bsp_GetKey
*	功能说明: 从按键FIFO缓冲区读取一个键值。
*	形    参: 无
*	返 回 值: 键值
*********************************************************************************************************
*/
uint8_t bsp_GetKey(void)
{
	uint8_t ret;

	if (s_tKey.Read == s_tKey.Write)
	{
		return KEY_NONE;
	}
	else
	{
		ret = s_tKey.Buf[s_tKey.Read];

		if (++s_tKey.Read >= KEY_FIFO_SIZE)
		{
			s_tKey.Read = 0;
		}
		return ret;
	}
}

/*
*********************************************************************************************************
*	函 数 名: bsp_GetKey2
*	功能说明: 从按键FIFO缓冲区读取一个键值，不会改动读指针。
*	形    参:  无
*	返 回 值: 键值
*********************************************************************************************************
*/
uint8_t bsp_GetKey2(void)
{
	uint8_t ret;

	if (s_tKey.Read2 == s_tKey.Write)
	{
		return KEY_NONE;
	}
	else
	{
		ret = s_tKey.Buf[s_tKey.Read2];

		if (++s_tKey.Read2 >= KEY_FIFO_SIZE)
		{
			s_tKey.Read2 = 0;
		}
		return ret;
	}
}

/*
*********************************************************************************************************
*	函 数 名: bsp_GetKeyState
*	功能说明: 获取按键状态
*	形    参:  _ucKeyID : 按键ID，从0开始
*	返 回 值: 1 表示按下， 0 表示未按下
*********************************************************************************************************
*/
uint8_t bsp_GetKeyState(KEY_ID_E _ucKeyID)
{
	return s_tBtn[_ucKeyID].State;
}

/*
*********************************************************************************************************
*	函 数 名: bsp_SetKeyParam
*	功能说明: 设置按键参数
*	形    参: _ucKeyID : 按键ID，从0开始
*			 _LongTime : 长按事件时间
*			 _RepeatSpeed : 连发速度
*	返 回 值: 无
*********************************************************************************************************
*/
void bsp_SetKeyParam(uint8_t _ucKeyID, uint16_t _LongTime, uint8_t _RepeatSpeed)
{
	s_tBtn[_ucKeyID].LongTime = _LongTime;		 /* 长按时长 0 表示不检测长按事件 */
	s_tBtn[_ucKeyID].RepeatSpeed = _RepeatSpeed; /* 连发速度，0表示不支持连发 */
	s_tBtn[_ucKeyID].RepeatCount = 0;			 /* 连发计数器 */
}

/*
*********************************************************************************************************
*	函 数 名: bsp_ClearKey
*	功能说明: 清空按键FIFO缓冲区
*	形    参: 无
*	返 回 值: 按键值
*********************************************************************************************************
*/
void bsp_ClearKey(void)
{
	s_tKey.Read = s_tKey.Write;
}

/*
*********************************************************************************************************
*	函 数 名: bsp_DetectKey
*	功能说明: 检测一个按键，更新状态机，该函数被10ms调用一次。
*	形    参: IO的id号，从0开始编号
*	返 回 值: 无
*********************************************************************************************************
*/
static void bsp_DetectKey(uint8_t i)
{
	KEY_T *pBtn;

	pBtn = &s_tBtn[i];
	if (IsKeyDownFunc(i))
	{
		if (pBtn->Count < KEY_FILTER_TIME)
		{
			pBtn->Count = KEY_FILTER_TIME;
		}
		else if (pBtn->Count < 2 * KEY_FILTER_TIME)
		{
			pBtn->Count++;
		}
		else
		{
			if (pBtn->State == 0)
			{
				pBtn->State = 1;

				/* 发送按键按下的消息 */
				bsp_PutKey((uint8_t)(3 * i + 1));
			}

			if (pBtn->LongTime > 0)
			{
				if (pBtn->LongCount < pBtn->LongTime)
				{
					/* 发送按键长按的消息 */
					if (++pBtn->LongCount == pBtn->LongTime)
					{
						/* 长按只发送一次 */
						bsp_PutKey((uint8_t)(3 * i + 3));
					}
				}
				else
				{
					if (pBtn->RepeatSpeed > 0)
					{
						if (++pBtn->RepeatCount >= pBtn->RepeatSpeed)
						{
							pBtn->RepeatCount = 0;
							/* 连发时每次10ms发送1个按键 */
							bsp_PutKey((uint8_t)(3 * i + 1));
						}
					}
				}
			}
		}
	}
	else
	{
		if (pBtn->Count > KEY_FILTER_TIME)
		{
			pBtn->Count = KEY_FILTER_TIME;
		}
		else if (pBtn->Count != 0)
		{
			pBtn->Count--;
		}
		else
		{
			if (pBtn->State == 1)
			{
				pBtn->State = 0;

				/* 发送按键弹起的消息 */
				bsp_PutKey((uint8_t)(3 * i + 2));
			}
		}

		pBtn->LongCount = 0;
		pBtn->RepeatCount = 0;
	}
}

/*
*********************************************************************************************************
*	函 数 名: bsp_DetectFastIO
*	功能说明: 检测快速的按键IO。1ms刷新一次
*	形    参: IO的id号，从0开始编号
*	返 回 值: 无
*********************************************************************************************************
*/
static void bsp_DetectFastIO(uint8_t i)
{
	KEY_T *pBtn;

	pBtn = &s_tBtn[i];
	if (IsKeyDownFunc(i))
	{
		if (pBtn->State == 0)
		{
			pBtn->State = 1;

			/* 发送按键按下的消息 */
			bsp_PutKey((uint8_t)(3 * i + 1));
		}

		if (pBtn->LongTime > 0)
		{
			if (pBtn->LongCount < pBtn->LongTime)
			{
				/* 发送按键长按的消息 */
				if (++pBtn->LongCount == pBtn->LongTime)
				{
					/* 长按只发送一次 */
					bsp_PutKey((uint8_t)(3 * i + 3));
				}
			}
			else
			{
				if (pBtn->RepeatSpeed > 0)
				{
					if (++pBtn->RepeatCount >= pBtn->RepeatSpeed)
					{
						pBtn->RepeatCount = 0;
						/* 连发时每次10ms发送1个按键 */
						bsp_PutKey((uint8_t)(3 * i + 1));
					}
				}
			}
		}
	}
	else
	{
		if (pBtn->State == 1)
		{
			pBtn->State = 0;

			/* 发送按键弹起的消息 */
			bsp_PutKey((uint8_t)(3 * i + 2));
		}

		pBtn->LongCount = 0;
		pBtn->RepeatCount = 0;
	}
}

/*
*********************************************************************************************************
*	函 数 名: bsp_KeyScan10ms
*	功能说明: 扫描所有按键输入，该函数被systick中断服务程序调用，每10ms一次
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/
void bsp_KeyScan10ms(void)
{
	uint8_t i;

	for (i = 0; i < KEY_COUNT; i++)
	{
		bsp_DetectKey(i);
	}
}

/*
*********************************************************************************************************
*	函 数 名: bsp_KeyScan1ms
*	功能说明: 扫描所有按键输入，该函数被systick中断服务程序调用，每1ms一次.
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/
void bsp_KeyScan1ms(void)
{
	uint8_t i;

	for (i = 0; i < KEY_COUNT; i++)
	{
		bsp_DetectFastIO(i);
	}
}
/*
*********************************************************************************************************
*	函 数 名: void main()
*	功能说明: 主函数演示如何使用.
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/
// void main()
// {
// 	uint8_t ucKeyCode; /* 按键代码 */
// 	bsp_InitKey(void);
// 	while (1)
// 	{
// 		if (time_10ms_flag)
// 		{
// 			time_10ms_flag = 0;
// 			bsp_KeyScan10ms(); // 此函数用于检测具有消抖功能的按键变化，需在systick中断中调用
// 		}
// 		if (time_1ms_flag)
// 		{
// 			time_1ms_flag = 0;
// 			bsp_KeyScan1ms(); // 此函数用于检测外部中断芯片来的按键信号，具有去抖等功能
// 		}
// 		/* 消抖和连击等处理完成后台systick中断服务程序实现，因此只需要调用bsp_GetKey获取键值即可。 */
// 		ucKeyCode = bsp_GetKey(); /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
// 		if (ucKeyCode != KEY_NONE)
// 		{
// 			switch (ucKeyCode)
// 			{
// 			case KEY_DOWN_K1: /* K1按下 */
// 				printf("K1按下\r\n");
// 				break;

// 			case KEY_UP_K1: /* K1弹起 */
// 				printf("K1弹起\r\n");
// 				break;

// 			case KEY_DOWN_K2: /* K2按下 */
// 				printf("K2按下\r\n");
// 				break;

// 			case KEY_UP_K2: /* K2弹起 */
// 				printf("K2弹起\r\n");
// 				break;

// 			case KEY_DOWN_K3: /* K3按下 */
// 				printf("K3按下\r\n");
// 				break;

// 			case KEY_UP_K3: /* K3弹起 */
// 				printf("K3弹起\r\n");
// 				break;

// 			case JOY_DOWN_U: /* 摇杆UP按下 */
// 				printf("摇杆上按下\r\n");
// 				break;

// 			case JOY_DOWN_D: /* 摇杆DOWN按下 */
// 				printf("摇杆下按下\r\n");
// 				break;

// 			case JOY_DOWN_L: /* 摇杆LEFT按下 */
// 				printf("摇杆左按下\r\n");
// 				break;

// 			case JOY_LONG_L: /* 摇杆LEFT长按 */
// 				printf("摇杆左长按\r\n");
// 				break;

// 			case JOY_DOWN_R: /* 摇杆RIGHT按下 */
// 				printf("摇杆右按下\r\n");
// 				break;

// 			case JOY_LONG_R: /* 摇杆RIGHT长按 */
// 				printf("摇杆右长按\r\n");
// 				break;

// 			case JOY_DOWN_OK: /* 摇杆OK按下 */
// 				printf("摇杆OK按下\r\n");
// 				break;

// 			case JOY_UP_OK: /* 摇杆OK弹起 */
// 				printf("摇杆OK弹起\r\n");
// 				break;

// 			case SYS_DOWN_K1K2: /* 摇杆OK按下 */
// 				printf("K1和K2组合键按下\r\n");
// 				break;

// 			default:
// 				/* 其他的值也不会出现 */
// 				break;
// 			}
// 		}
// 	}
// }
/***************************** (END OF FILE) *********************************/
